
/**
*	\file
*	Defines the sun::window class.
*	\author Sean Tromnas
*/

#ifndef	SUN_WINDOW_HPP
#define	SUN_WINDOW_HPP

#include <sun/disable_warnings.hpp>
#include <sun/input/buttons.hpp>
#include <sun/input/analogues.hpp>
#include <sun/lua/lua_fwd.hpp>

#include <rainbow/graphics_device/graphics_device.hpp>

#include <needs_more_ninja/template/singleton/singleton.hpp>
#include <boost/scoped_ptr.hpp>

SUN_PUSH_DISABLE_SIGNALS_WARNINGS( )
#include <boost/signals2.hpp>
SUN_POP_DISABLED_WARNINGS( )

namespace sun
{

	namespace detail
	{

		/** Signal combiner to return whether any slot handled a system signal. */
		struct window_slot_combiner
		{
			typedef bool result_type;

			template<typename InputIterator>
			bool operator( )( InputIterator first, InputIterator last ) const;
		};

	};

	/**
	 *	Singleton object for the window owned by the application.
	 *	Handles converting the OS messages into events which can be used by the rest of the application.
	 *	\author Sean Tromans
	 */
	class window : public nmn::singleton<window>
	{
		//
		//	Public Member Types
		//
		public:

			/** Collection of OpenGL extension names. */
			typedef std::vector< std::string >	extension_name_vector_type;

			/** Settings used to create the application window. */
			struct window_settings
			{
				window_settings( );

				std::string		title;			///<	The string to appear in the title bar of the window if available.
				unsigned int	width;			///<	The width of the window.
				unsigned int	height;			///<	The height of the window.
				unsigned int	colour_bits;	///<	How many bits to use for the colour data of the window.
				unsigned int	depth_bits;		///<	How many bits to use for the windows depth buffer.
				unsigned int	stencil_bits;	///<	How many bits to use for the stencil buffer.
				unsigned int	samples;		///<	How many samples should be performed per pixel. 0 or 1 if multisampling should be disabled.
				bool			fullscreen;		///<	Whether the window should be full screen or not.

				extension_name_vector_type	extensions;	///<	The additional extensions the application requires to run.
														///<	By default sun checks for OpenGL 2.1, GLSL 1.3 and ARB framebuffer.
			};

			/** The signal which will be fired when the windows gains or loses focus. */
			typedef	boost::signals2::signal< bool ( bool focused ), detail::window_slot_combiner > focus_signal_type;

			/** The signal which will be fired when the user closes the application window. */
			typedef	boost::signals2::signal< bool ( ), detail::window_slot_combiner > closed_signal_type;

			/** The signal which will be fired when the user makes an analogue input (e.g. mouse move). */
			typedef	boost::signals2::signal< bool ( analogues::enum_ analogue, float delta ), detail::window_slot_combiner > analogue_signal_type;

			/** The signal which will be fired when the user presses or releases a button. */
			typedef	boost::signals2::signal< bool ( buttons::enum_ button, bool pressed ), detail::window_slot_combiner > button_signal_type;

			/** A connection handler to a system signal slot. */
			typedef	boost::signals2::connection	signal_connection_type;

		//
		//	Public Member Functions
		//
		public:

			/**
			*	Default constructor.
			*	\author Sean Tromans
			*/
			window( );

			/**
			*	Deconstructor.
			*	\author Sean Tromans
			*/
			~window( );

			/**
			*	Attempts to create the application window with the desired settings.
			*	@param settings The settings to use when creating the window.
			*	@return false if the window could not be created.
			*	\author Sean Tromans
			*/
			bool create_window( window_settings const & settings );

			/**
			Returns a const reference to the rainbow::graphics_device used by this window.
			\author Sean Tromans
			*/
			rainbow::graphics_device const & device( ) const
			{
				return m_device;
			}

			/**
			Returns a non-const reference to the rainbow::graphics_device used by this window.
			\author Sean Tromans
			*/
			rainbow::graphics_device & device( )
			{
				return m_device;
			}

			/**
			Register a new slot to be notified when the window gains or loses focus.
			@param slot The new slot to attach to the signal.
			\author Sean Tromans
			*/
			signal_connection_type notify_focus_change( focus_signal_type::slot_type const & slot );

			/**
			Register a new slot to be notified when the user closes the window.
			@param slot The new slot to attach to the signal.
			\author Sean Tromans
			*/
			signal_connection_type notify_quit( closed_signal_type::slot_type const & slot );

			/**
			Register a new slot to be notified when an analogue input is received.
			@param slot The new slot to attach to the signal.
			\author Sean Tromans
			*/
			signal_connection_type notify_analogue_changed( analogue_signal_type::slot_type const & slot );

			/**
			Register a new slot to be notified whenever a button is pressed or released.
			@param slot The new slot to attach to the signal.
			\author Sean Tromans
			*/
			signal_connection_type notify_button_changed( button_signal_type::slot_type const & slot );

			/**
			Enables or disables the default cursor.
			@param show Whether the cursor should be active or not.
			\author Sean Tromans
			*/
			void show_cursor( bool show );

			/**
			Locks the cursor to stay within this window.
			@param lock Whether the cursor should be locked or not.
			\author Sean Tromans
			*/
			void grab_cursor( bool lock );

			/**
			*	Translates all the currently waiting system messages.
			*	\author Sean Tromans
			*/
			void pump_messages( );

			/**
			Refreshes the window with the most recently drawn image.
			\author Sean Tromans
			*/
			void present( ) const;

			/**
			*	Destroys the window cleanly to either exit the application, or to recreate another window.
			*	\author Sean Tromans
			*/
			void close_window( );

		//
		//	Private Member Types
		//
		private:

			/** Private, platform specific implementation of the window class. */
			struct impl;

		//
		//	Private Member Variables
		//
		private:

			boost::scoped_ptr<impl>	m_impl;
			rainbow::graphics_device m_device;

		//
		//	Static Member Functions
		//
		public:

			/**
			Exposes the window singleton to lua.
			@param  L The lua_State being initialised.
			\author Sean Tromans
			*/
			static void lua_interface( lua_State * L );
	};

};

#include <sun/window/window.inl>

#endif	//SUN_WINDOW_HPP
